private import SDL;
private import SDL_mixer;
private import opengl;
private import openglu;
private import SDLBridge.SDLGraphics;
private import SDLBridge.SDLSprite;
private import SDLBridge.SDLSound;
private import Sound.Manager;
private import System.BootInfo;
private import Form.BaseForm;
private import MainForm;
private import Input.GamePad;

private import System.Console;
private import std.string;

private import Graphics.IGraphics;
// http://www.tacoworks.jp/software/SDLdoc-jp/html/



int SDLsys( string[] args ){
	Console.writeln("Hello , SDL!");

	BootInfo boot;
	SDLGraphics g;
	SDL_Joystick *joystick;
	BaseForm form;


	SDLGraphics initScreen( int width , int height , int glFlags = 0 , bool fullscreen = false){
		Console.write("Init Screen...");
		if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
			throw new Error("Couldn't initialize SDL VIDEO");
		else
			Console.writeln("OK");

		if( fullscreen ) glFlags |= SDL_FULLSCREEN;
		glFlags |= SDL_HWSURFACE |  /*SDL_DOUBLEBUF |*/ SDL_OPENGL;
/*
		SDL_GL_SetAttribute( SDL_GL_RED_SIZE     , 8 );
		SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE   , 8 );
		SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE    , 8 );
		SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE   ,16 );
*/
		SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER , 1 );

		SDL_Surface *screen = SDL_SetVideoMode( width , height , 0 , glFlags);
		if ( screen == null )
			screen = SDL_SetVideoMode( width = 640 , height = 480 , 0 , SDL_OPENGL );
		if ( screen == null )
			throw new Error("Couldn't set video mode");

		if ( fullscreen ) SDL_ShowCursor(SDL_DISABLE);


		glClearColor( 0.0f , 0.0f , 0.0f , 0.0f );


		glDisable(GL_TEXTURE_2D);
		glDisable(GL_COLOR_MATERIAL);
		glDisable(GL_LIGHTING);
		glDisable(GL_CULL_FACE);
		glEnable(GL_BLEND);

		SDLGraphics g = new SDLGraphics( screen , width , height );
		return g;
	} //

	SDL_Joystick *initJoystick(){
		Console.write("Init joystick...");
		if( SDL_Init( SDL_INIT_JOYSTICK ) < 0 )
			Console.write("Couldn't initialize SDL JOYSTICK");
		else
			Console.writeln("OK");
		
		for ( int i=0; i < SDL_NumJoysticks(); i++ ) {
			Console.write("Joystick:%d\t%s\n", i , SDL_JoystickName(i));
		}
		SDL_JoystickEventState( SDL_ENABLE );
		SDL_Joystick *ret = SDL_JoystickOpen( 0 );
		return ret;
	} //
	bool initAudio( bool mute ) {
		Console.write("Init Audio...");
		ISoundSprite dummy() {return DummySoundSprite.instance;}
		ISoundSprite create(){return new SDLSoundSprite(); }

		if ( mute || SDL_Init( SDL_INIT_AUDIO ) < 0 || Mix_OpenAudio( 44100 , AUDIO_S16 , 2 , 4096 ) < 0 ) {
			// 初期化失敗時はダミークラスを渡すので、以降のプログラムは成功/失敗を意識しなくてよい
			Console.write("disabled...");
			SoundManager.setSystem( DummySoundSprite.instance , &dummy  , 3 );
		} else {
			SoundManager.setSystem( SDLBackgroundMusic.instance   , &create , 6 );
		}
		Console.writeln("OK");
		return true;
	} //
	
	void setTitle(string caption , string file ){
		SDL_WM_SetCaption( toStringz(caption) , null );
		// アイコン設定
		version ( Win32 ) {
			if ( !( file is null ) ) {
				SDL_Surface* icon = SDL_LoadBMP( toStringz( file ) );
				SDL_WM_SetIcon(icon, null);
			}
		}
	}


	const int SDL_BUTTON_WHEELUP	= 4;
	const int SDL_BUTTON_WHEELDOWN	= 5;
	const int SDL_BUTTON_WHEELLEFT	= 6;
	const int SDL_BUTTON_WHEELRIGHT	= 7;
	int mouseWheelEvent( SDL_Event e , bool delegate( int x , int y , int deltaY , int deltaX ) mouseWheelCall ){
		int deltaY = 0 , deltaX = 0 ;

		switch(e.button.button){
			case SDL_BUTTON_WHEELUP:
				deltaY = -1;
			break;
			case SDL_BUTTON_WHEELDOWN:
				deltaY =  1;
			break;
			case SDL_BUTTON_WHEELLEFT:
				deltaX = -1;
			break;
			case SDL_BUTTON_WHEELRIGHT:
				deltaX =  1;
			break;
			default:
				break;
		}
		mouseWheelCall( e.motion.x , e.motion.y , deltaY , deltaX );
		return deltaY;
	} //
	int mouseButtonEvent( SDL_Event e 
						, bool delegate( int x , int y , int button              ) mouseButtonCall 
						, bool delegate( int x , int y , int deltaY , int deltaX ) mouseWheelCall
						){
		int btn = 0;
		int ex = e.motion.x , ey = e.motion.y;
			switch(e.button.button){
				case SDL_BUTTON_LEFT:
					btn = 1;
					mouseButtonCall( ex , ey , btn );
					break;
				case SDL_BUTTON_RIGHT:
					btn = 2;
					mouseButtonCall( ex , ey , btn );
					break;
				case SDL_BUTTON_WHEELUP:
				case SDL_BUTTON_WHEELDOWN:
				case SDL_BUTTON_WHEELLEFT:
				case SDL_BUTTON_WHEELRIGHT:
					mouseWheelEvent( e , mouseWheelCall );
					btn = 0;
					default:
					break;
			}
		return 1 << ( btn - 1 );
	} //
	int keyEvent( SDL_Event e , bool delegate( int keyCode ) keyboardCall ){
		int k = e.key.keysym.sym;
		switch( k ){
			case SDLK_F5:
			
				break;
			default:
				break;
		}
		keyboardCall( k );
		return k;
	} //

	bool joyAxisEvent( SDL_Event e , bool delegate( int deviceId , int axis , int delta ) joyAxisCall ){
		int axis  = 0 , delta = 0;
		int deviceId = e.jaxis.which;
		switch ( e.jaxis.axis ) {
			case 0:
			axis = JOYAXIS_X;
				break;
			case 1:
			axis = JOYAXIS_Y;
				break;
			default:
				break;
		}
		if ( e.jaxis.value >  3200 ) delta =  1;
		else
		if ( e.jaxis.value < -3200 ) delta = -1;

		return joyAxisCall( deviceId , axis , delta );
	} //
	

	int eventLoop( BaseForm form ){
		SDL_Event e;
		int mouseButton = 0;
		int f = 0;
		bool work = true;
		while ( work ) {
			while ( SDL_PollEvent(&e) ) {
				switch ( e.type ) {
					case SDL_MOUSEBUTTONDOWN:
						mouseButton |=  mouseButtonEvent( e , &form.onMouseDown , &form.onMouseWheel );
						break;
					case SDL_MOUSEBUTTONUP:
						mouseButton &= !mouseButtonEvent( e , &form.onMouseUp   , &form.onMouseWheel );
						break;
					case SDL_MOUSEMOTION:
						form.onMouseMove( e.motion.x , e.motion.y  , mouseButton );
						break;
					case SDL_KEYUP:
						keyEvent( e , &form.onKeyUp);
						break;
					case SDL_KEYDOWN:
						keyEvent( e , &form.onKeyDown);
						break;
					case SDL_JOYAXISMOTION:
						joyAxisEvent( e , &form.onJoyAxis );
						break;
					case SDL_JOYBUTTONDOWN:
						form.onJoyButtonDown( e.jaxis.which , e.jbutton.button );
						break;
					case SDL_JOYBUTTONUP:
						form.onJoyButtonUp( e.jaxis.which , e.jbutton.button );
						break;
					case SDL_QUIT:
						form.onExit();
						break;
					case SDL_ACTIVEEVENT:
						if ( (e.active.state & SDL_APPINPUTFOCUS) > 0 ) {
							if( e.active.gain == 1  )	form.onGetFocus(); // get? got?
							else						form.onLostFocus();
						}
						if ( (e.active.state & SDL_APPMOUSEFOCUS) > 0 ) {
							if( e.active.gain == 1  )	form.onMouseEnter( e.motion.x , e.motion.y );
							else						form.onMouseLeave();
						}
						break;
					default:
						break;
				}
			}
			while( (f = form.pollEvent() ) != 0 ) {
				switch ( f ) {
					case FORMEVENT_SETCAPTION:
						setTitle( form.caption , null );
						break;
					case FORMEVENT_QUIT:
						Console.writeln("FORMEVENT_QUIT");
						work = false;
						break;
					default:
						break;
				}
			}
			SDL_Delay( 1000 / 60 );

			form.onTimer();
		}
		return 0;
	} //

	void quitSDL( SDLGraphics g , SDL_Joystick *j ){
		g.dispose();
		Mix_HaltMusic();
		Mix_CloseAudio();
		if ( !( j is null ) ) SDL_JoystickClose( j );
		SDL_Quit();
	} //


	boot = new BootInfo( args );
	g = initScreen( boot.screenWidth , boot.screenHeight , SDL_OPENGL , boot.isFullscreen );
	setTitle(  SystemConfig.getParam("TITLE" , "Explode Runner" ) , "resource/hell.bmp" );
	joystick = initJoystick();
	initAudio( boot.isMuted );

	form = new MainForm( boot , g );
	form.init();

		int ret = eventLoop( form );

	form.close();
	quitSDL( g , joystick );
	return ret;
} //



version ( Win32_release ) {
	private import std.c.windows.windows;
	private import std.string;

	extern (C) void gc_init();
	extern (C) void gc_term();
	extern (C) void _minit();
	extern (C) void _moduleCtor();

	extern (Windows)
	public int WinMain(HINSTANCE hInstance,
		HINSTANCE hPrevInstance,
		LPSTR lpCmdLine,
		int nCmdShow)
	{
		int result;
		gc_init();
		_minit();
		try
		{
			_moduleCtor();
			char exe[4096];
			GetModuleFileNameA(null, cast(char*)exe, 4096);
			string[1] prog;
			prog[0] = std.string.toString(toStringz(exe));
			result  = SDLsys(prog ~ std.string.split(std.string.toString(lpCmdLine)));
		}
		catch(Object o)
		{
			printf(std.string.toStringz(o.toString()));
			result = 1;
		}
		gc_term();
		return result;
	}
} else version(Mac_release) {
	extern(C) void gc_init();
	extern(C) void gc_term();
	extern(C) void _moduleCtor();
	extern(C)
	int SDL_main(int argc, char **argv)
	{
		int result;
		gc_init();
		try
		{
			_moduleCtor();
			{
				string[] args;
				args.length = argc;
				for(int i=0; i<argc; i++)
				{
					args[i] = std.string.toString(argv[i]);
				}
				result = SDLsys(args);
			}
		}
		catch(Object o)
		{
			printf(std.string.toStringz(o.toString()));
			result = 1;
		}
		gc_term();
		return result;
	}
} else {
	public int main(string[] args)
	{
		return SDLsys(args);
	}
}

